package service

import (
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/bitly/go-simplejson"

	"im/common"
	"im/errcode"
	"im/model"
	"im/requests"
)

const rongYunBaseUrl = "https://api-cn.ronghub.com"
const (
	// 用户
	rongYunUserGetTokenPath = "/user/getToken.json"
	rongYunUserRefreshPath  = "/user/getToken.json"

	// 群组
	rongYunGroupCreatePath  = "/group/create.json"
	rongYunGroupDismissPath = "/group/dismiss.json"
	rongYunGroupJoinPath    = "/group/join.json"
	rongYunGroupQuitPath    = "/group/quit.json"
	rongYunGroupRefreshPath = "/group/refresh.json"
)

type RongYun interface {
	// 用户相关
	GetToken(user model.Users) (string, error)
	UpdateUser(rongyunUserId string, data requests.UsersPutParams) error

	// 群组相关
	CreateGroup(groupId int64, groupName string, userIds ...string) error
	DismissGroup(groupId int64, rUserId string) error
	JoinGroup(groupId int64, groupName string, rUserIds ...string) error
	QuitGroup(groupId int64, rUserIds ...string) error
	RefreshGroup(groupIds int64, groupName string) error
}

func NewRongYun() RongYun {
	return rongYun{}
}

type rongYun struct {
}

func (r rongYun) RefreshGroup(groupId int64, groupName string) error {
	var body = url.Values{}
	body.Add("groupId", strconv.FormatInt(groupId, 10))
	body.Add("groupName", groupName)
	_, err := r.postForm(rongYunGroupRefreshPath, body)
	return err
}

func (r rongYun) JoinGroup(groupId int64, groupName string, rUserIds ...string) error {
	var body = url.Values{}
	body.Add("groupId", strconv.FormatInt(groupId, 10))
	body.Add("groupName", groupName)
	for _, rUserId := range rUserIds {
		body.Add("userId", rUserId)
	}
	_, err := r.postForm(rongYunGroupJoinPath, body)
	return err
}

func (r rongYun) QuitGroup(groupId int64, rUserIds ...string) error {
	var body = url.Values{}
	body.Add("groupId", strconv.FormatInt(groupId, 10))
	for _, rUserId := range rUserIds {
		body.Add("userId", rUserId)
	}
	_, err := r.postForm(rongYunGroupQuitPath, body)
	return err
}

func (r rongYun) DismissGroup(groupId int64, rUserId string) error {
	var body = url.Values{}
	body.Add("groupId", strconv.FormatInt(groupId, 10))
	body.Add("userId", rUserId)
	_, err := r.postForm(rongYunGroupDismissPath, body)
	return err
}

func (r rongYun) CreateGroup(groupId int64, groupName string, rUserIds ...string) error {
	var body = url.Values{}
	body.Add("groupId", strconv.FormatInt(groupId, 10))
	body.Add("groupName", groupName)
	for _, rUserId := range rUserIds {
		body.Add("userId", rUserId)
	}
	_, err := r.postForm(rongYunGroupCreatePath, body)
	return err
}

func (r rongYun) UpdateUser(rongyunUserId string, data requests.UsersPutParams) error {
	var body = url.Values{}
	body.Add("userId", rongyunUserId)
	if data.Nickname != "" {
		body.Add("name", data.Nickname)
	}
	if data.Avatar != "" {
		body.Add("portraitUri", data.Avatar)
	}
	_, err := r.postForm(rongYunUserRefreshPath, body)
	return err
}

func (r rongYun) GetToken(user model.Users) (string, error) {
	var body = url.Values{}
	body.Add("userId", strconv.FormatInt(user.Id, 10))
	body.Add("name", user.Nickname)
	body.Add("portraitUri", user.Avatar)
	resp, err := r.postForm(rongYunUserGetTokenPath, body)
	if err != nil {
		return "", err
	}
	return resp.Get("token").MustString(), nil
}

func (r rongYun) postForm(path string, body url.Values) (*simplejson.Json, error) {
	req, err := http.NewRequest("POST", rongYunBaseUrl+path, strings.NewReader(body.Encode()))
	if err != nil {
		return nil, errcode.NewError(errcode.RongyunErr, err)
	}

	header := r.geneHeader()
	req.Header = header
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, errcode.NewError(errcode.RongyunErr, err)
	}
	defer resp.Body.Close()

	result, err := simplejson.NewFromReader(resp.Body)
	if err != nil {
		return nil, errcode.NewError(errcode.RongyunErr, err)
	}

	if code, err := result.Get("code").Int(); err != nil || code != 200 {
		return nil, errcode.NewError(errcode.RongyunErr,
			errors.New(fmt.Sprintf("%+v", result)))
	}
	return result, nil
}

func (r rongYun) geneHeader() http.Header {
	// header
	rand.Seed(time.Now().UnixNano())
	appSecret := os.Getenv("RONGYUN_APPSECRET")
	nonce := rand.Int()
	timestamp := time.Now().Nanosecond()
	signature := common.Sha1(fmt.Sprintf("%s%d%d", appSecret, nonce, timestamp), -1)
	header := http.Header{}
	header.Add("App-Key", os.Getenv("RONGYUN_APPKEY"))
	header.Add("Nonce", strconv.Itoa(nonce))
	header.Add("Timestamp", strconv.Itoa(timestamp))
	header.Add("Signature", signature)
	header.Set("Content-Type", "application/x-www-form-urlencoded")
	return header
}
